package org.hackystat.ui.projectviewer.gwt.client.panels;

import org.hackystat.ui.projectviewer.gwt.client.CSSStyles;
import org.hackystat.ui.projectviewer.gwt.client.common.MainPanelWidget;
import org.hackystat.ui.projectviewer.gwt.client.common.Session;
import org.hackystat.ui.projectviewer.gwt.client.common.types.ProjectData;
import org.hackystat.ui.projectviewer.gwt.client.common.types.ProjectDevSummary;
import org.hackystat.ui.projectviewer.gwt.client.dataprovider.DataProviderMediator;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.ClickListenerCollection;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SourcesClickEvents;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Displays project-related telemetry charts.
 *
 * @author Pavel Senin.
 *
 */
public class ProjectChartPanel extends MainPanelWidget implements ClickListener,
  SourcesClickEvents {

  private final ClickListenerCollection clickListeners = new ClickListenerCollection();

  /** {@literal} SMALL_WAIT_IMAGE icon. */
  public static final String SMALL_WAIT_IMAGE = "images/wait-animated-16.gif";
  final Image waitImage1 = new Image(SMALL_WAIT_IMAGE);
  final Image waitImage2 = new Image(SMALL_WAIT_IMAGE);

  /* the outer and inner frames */
  private final VerticalPanel outer = new VerticalPanel();
  private final VerticalPanel inner = new VerticalPanel();

  /* the project name Title */
  private final HorizontalPanel projectTitle = new HorizontalPanel();
  private final HTML projectTitleHTML = new HTML("Project charts panel");

  /* the project devTime chart */
  private final VerticalPanel projectDevTimePane = new VerticalPanel();
  private final HTML devTimeTitle = new HTML("Development time:");

  private String currPrjName;
  private ProjectData currPrjData;

  // needed for time measurements
  private long startTimeMillis;

  private static final String DL = "|";
  private static final String DM = "d,ff9900";
  private static final String CM = ",";

  /**
   * Creates an instance and reports a handler.
   *
   * @return The prjectData panel instance.
   */
  public static MPHandler init() {
    return new MPHandler("projectChart") {
      public MainPanelWidget createInstance() {
        return new ProjectChartPanel();
      }
    };
  }

  /**
   * Instantiates the panel.
   *
   */
  public ProjectChartPanel() {

    // making up panels and wait images
    outer.setStyleName(CSSStyles.INFO_PANEL_STYLE);
    outer.setVerticalAlignment(VerticalPanel.ALIGN_TOP);
    outer.add(inner);
    inner.setWidth("100%");
    waitImage1.setStyleName(CSSStyles.INFO_PANEL_WAIT_SMALL_STYLE);
    waitImage2.setStyleName(CSSStyles.INFO_PANEL_WAIT_SMALL_STYLE);

    // laying down all panels and tables
    projectTitle.add(projectTitleHTML);
    projectTitle.setStyleName(CSSStyles.INFO_PANEL_TITLE_STYLE);

    projectDevTimePane.setStyleName(CSSStyles.INFO_PANEL_CHART_IMAGE_STYLE);
    projectDevTimePane.add(devTimeTitle);

    inner.add(projectTitle);
    inner.add(projectDevTimePane);

    initWidget(outer);
  }

  /**
   * Set the info for the project specified.
   *
   * @param projectId The project name.
   */
  public void setProject(final String projectId) {
    if (DataProviderMediator.getInstance().lock()) {
      // do a little panel cleanup first
      projectTitle.insert(waitImage1, 0);
      projectTitleHTML.setHTML("<h3> getting data ... </h3>");
      projectDevTimePane.setVisible(false);
      if (projectDevTimePane.getWidgetCount() > 1) {
        projectDevTimePane.remove(1);
      }
      // Setup the callback from the service.
      AsyncCallback<ProjectData> callback = new AsyncCallback<ProjectData>() {
        // If the call was successful, we will get back the result of authorization.
        public void onSuccess(ProjectData s) {
          DataProviderMediator.getInstance().unlock();
          if (Log.isDebugEnabled()) {
            long endTimeMillis = System.currentTimeMillis();
            float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
            Log.debug("PDP-Pane: unlocked. success in : " + durationSeconds + " seconds");
          }
          currPrjData = s;
          currPrjName = s.getName();
          projectTitle.remove(0);
          projectTitleHTML.setHTML("<h3>" + s.getName() + "</h3>");
          projectDevTimePane.setVisible(true);
          // get last sensorData
          setDevTime();
        }

        // Something went wrong with the call. Handle the issue how you'd like.
        public void onFailure(Throwable ex) {
          DataProviderMediator.getInstance().unlock();
          if (Log.isDebugEnabled()) {
            long endTimeMillis = System.currentTimeMillis();
            float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
            Log.debug("PDP-Pane: unlocked. failure in : " + durationSeconds + " seconds");
          }
          Window.alert("Unknown exception happend during RPC call. \n Please provide feedback "
              + "to hackystat-dev@googlegroups.com. \n Error: " + ex.getMessage());
        }
      };
      if (Log.isDebugEnabled()) {
        startTimeMillis = System.currentTimeMillis();
        Log.debug("PDP-Pane: RPC locked. getting project info for " + projectId);
      }
      DataProviderMediator.getInstance().getProjectData(projectId, callback);
    }
    else {
      Window.alert("RPC is locked, unable to proceed with communications. \n Please provide "
          + "feedback to hackystat-dev@googlegroups.com.");
    }
  }

  /**
   * Sets summaries and links to download the data.
   */
  private void setDevTime() {
    if (DataProviderMediator.getInstance().lock()) {
      // Setup the callback from the service.
      AsyncCallback<ProjectDevSummary> callback = new AsyncCallback<ProjectDevSummary>() {
        // If the call was successful, we will get back the result of authorization.
        public void onSuccess(ProjectDevSummary s) {
          DataProviderMediator.getInstance().unlock();
          if (Log.isDebugEnabled()) {
            long endTimeMillis = System.currentTimeMillis();
            float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
            Log.debug("PDP-Pane: unlocked. success in : " + durationSeconds + " seconds");
          }
          // projectTitle.remove(0);
          // projectTitleHTML.setHTML("<h3>" + currPrjData.getName() + " in charts: </h3>");
          devTimeTitle.setHTML(currPrjName + " development time for "
              + Session.getInstance().getUser());
          formatDevTime(s);
        }

        // Something went wrong with the call. Handle the issue how you'd like.
        public void onFailure(Throwable ex) {
          DataProviderMediator.getInstance().unlock();
          if (Log.isDebugEnabled()) {
            long endTimeMillis = System.currentTimeMillis();
            float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
            Log.debug("PDP-Pane: unlocked. failure in : " + durationSeconds + " seconds");
          }
          Window.alert("Unknown exception happend during RPC call. \n Please provide feedback "
              + "to hackystat-dev@googlegroups.com. \n Error: " + ex.getMessage());
        }
      };
      if (Log.isDebugEnabled()) {
        startTimeMillis = System.currentTimeMillis();
        Log.debug("PDP-Pane: RPC locked. getting data summaries for " + currPrjName);
      }
      DataProviderMediator.getInstance().getProjectDevTimeData(currPrjName, currPrjData.getOwner(),
          callback);
    }
    else {
      Window.alert("RPC is locked, unable to proceed with communications. \n Please provide "
          + "feedback to hackystat-dev@googlegroups.com.");
    }
  }

  /**
   * Formats chart and puts an show.
   *
   * @param s The data summary to process.
   */
  private void formatDevTime(ProjectDevSummary s) {
    if (Log.isDebugEnabled()) {
      Log.debug("PCP-Pane: got data, making chart.");
    }
    Float[] data = s.getValues();
    String[] caps = s.getCaps();

    // now, let's make a pie chart
    if (caps.length > 0) {
      StringBuffer sb = new StringBuffer(1000);
      // standard url
      sb.append("http://chart.apis.google.com/chart?");
      // chart type
      sb.append("cht=lc");
      // chart size
      sb.append("&chs=450x250");
      // axis and chart line style
      sb.append("&chxt=x,y&chxs=0,0000dd,10|3,0000dd,12,1&chls=2,6,2");
      // data
      sb.append("&chd=" + encodeData(data));
      // captions
      sb.append("&chxl=0:");
      for (int i = 0; i < caps.length; i++) {
        sb.append(DL + caps[i]);
      }
      sb.append(DL);
      // y axis marks
      Float maxV = Float.MIN_VALUE;
      for (int i = 0; i < data.length; i++) {
        if (data[i] > maxV) {
          maxV = data[i];
        }
      }
      Float step = maxV / 5;
      NumberFormat df = NumberFormat.getFormat("#.#");
      String yCaps = "|0|" + df.format(step) + DL + df.format(step * 2) + DL + df.format(step * 3)
          + DL + df.format(step * 4) + DL + df.format(step * 5);
      sb.append("1:" + yCaps);
      //
      // the "diamonds"
      // chm=<marker type>,<color>,<data set index>,<data point>,<size>|
      sb.append("&chm=");
      for (int i = 0; i < caps.length; i++) {
        sb.append(DM + CM + "0" + CM + i + CM + "10.0");
        if (i < caps.length - 1) {
          sb.append(DL);
        }
      }
      // tiny grid lines
      //
      sb.append("&chg=14.3,20,1,5");
      // title
      // sb.append("&chtt=Development+Time");

      if (Log.isDebugEnabled()) {
        Log.debug("Calling url: " + sb.toString());
      }
      projectDevTimePane.add(new Image(sb.toString()));
    }

  }

  // /**
  // * Formats project summary data into flex table.
  // *
  // * @param psd The project summary data.
  // */
  // private void formatSummaryTable(ProjectSummaryData psd) {
  // DateTimeFormat dtf = DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
  // dataGrid.clear();
  // dataGrid.setText(0, 0, "Total data entries");
  // dataGrid.setText(0, 1, psd.getDataInstances().toString());
  // Map<String, String> dSum = psd.getDataSummary();
  // Integer[] values = new Integer[dSum.size()];
  // String[] caps = new String[dSum.size()];
  // Session session = Session.getInstance();
  // int row = 1;
  // for (Entry<String, String> entry : dSum.entrySet()) {
  // String cap = entry.getKey();
  // dataGrid.setText(row, 0, cap);
  // caps[row - 1] = cap;
  // String value = entry.getValue();
  // dataGrid.setText(row, 1, value);
  // values[row - 1] = Integer.valueOf(value);
  // // Button downloadDataButton = new Button("download");
  // // downloadDataButton.setTitle(entry.getKey());
  // // downloadDataButton.addClickListener(this);
  // // dataGrid.setWidget(row, 2, downloadDataButton);
  // HTML link = new HTML("<a href=\"/hackystat-ui-projectviewer/fetchfileservlet?project="
  // + this.currPrjName + "&host=" + session.getTelemetryHost() + "&sdt=" + entry.getKey()
  // + "&user=" + session.getUser() + "&password=" + session.getPassword() + "&startDate="
  // + dtf.format(currPrjData.getStartTime()) + "&endDate="
  // + dtf.format(currPrjData.getEndTime()) + "\"target=\"_blank\">download " + entry.getKey()
  // + " data </a>");
  // dataGrid.setWidget(row, 2, link);
  // row++;
  // }
  // if (Log.isDebugEnabled()) {
  // Log.debug("PDP-Pane: constructed data retrieval links using:");
  // Log.debug("PDP-Pane: project Start: " + dtf.format(currPrjData.getStartTime()) + ", end: "
  // + dtf.format(currPrjData.getEndTime()));
  // }
  //
  // // now, let's make a pie chart
  // if (caps.length > 0) {
  // StringBuffer sb = new StringBuffer(1000);
  // // standard url
  // sb.append("http://chart.apis.google.com/chart?");
  // // chart type
  // sb.append("cht=p");
  // // chart size
  // sb.append("&chs=330x200");
  // // data
  // sb.append("&chd=" + encodeData(values));
  // // captions
  // sb.append("&chl=" + caps[0]);
  // for (int i = 1; i < caps.length; i++) {
  // sb.append("|" + caps[i]);
  // }
  // if (Log.isDebugEnabled()) {
  // Log.debug("Calling url: " + sb.toString());
  // }
  // dataChartpanel.add(new Image(sb.toString()));
  // }
  //
  // }
  //
  /**
   * Formats data to the google-cart API format.
   *
   * @param data The chart data.
   * @return Google-Chart API formatted data string.
   */
  public String encodeData(Float[] data) {
    String simpleEncoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    StringBuffer response = new StringBuffer(500);
    response.append("s:");
    // get maxData
    Float maxValue = Float.MIN_VALUE;
    for (int i = 0; i < data.length; i++) {
      if (data[i] > maxValue) {
        maxValue = data[i];
      }
    }
    // encode
    for (int i = 0; i < data.length; i++) {
      Float currentValue = data[i];
      if (currentValue >= 0) {
        response.append(simpleEncoding.charAt(Math.round((simpleEncoding.length() - 1)
            * currentValue / maxValue)));
      }
      else {
        response.append('_');
      }
    }
    return response.toString();
  }

  /**
   * {@inheritDoc}
   */
  public void addClickListener(ClickListener listener) {
    this.clickListeners.add(listener);
  }

  /**
   * {@inheritDoc}
   */
  public void removeClickListener(ClickListener listener) {
    this.clickListeners.remove(listener);
  }

  /**
   * Reports current project selection.
   *
   * @return The current project selection.
   */
  public String getProjectSelection() {
    return this.currPrjName;
  }

  /**
   * {@inheritDoc}
   */
  public void onClick(Widget sender) {
    assert true;
  }

}
